package com.magina.common.log;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.magina.basic.model.SysLog;
import com.magina.common.constant.ProjectConstants;
import com.magina.common.service.SysLogConsumerService;
import cz.mallat.uasparser.OnlineUpdater;
import cz.mallat.uasparser.UASparser;
import cz.mallat.uasparser.UserAgentInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;


@Slf4j
@Aspect
@Component
@Order(-5)
public class SystemLogAspect {

    /**
     * 操作日志记录Mapper
     */
    @Autowired
    private SysLogConsumerService sysLogConsumerService;

    /**
     * 方法描述: [定义service切入点拦截规则，拦截LogEvent注解的方法.]</br>
     * 初始作者: br/>
     * 创建日期: 2019年3月29日-下午7:39:31<br/>
     * 开始版本: 2.0.0<br/>
     * =================================================<br/>
     * 修改记录：<br/>
     * 修改作者 日期 修改内容<br/>
     * ================================================<br/>
     * void
     *
     */
    @Pointcut("@annotation(com.magina.common.log.LogEvent)")
    public void serviceAspect() {
    }

    /**
     * 方法描述: [拦截控制层的操作日志.]</br>
     * 初始作者: <br/>
     * 创建日期: 2019年3月29日-下午7:40:03<br/>
     * 开始版本: 2.0.0<br/>
     * =================================================<br/>
     * 修改记录：<br/>
     * 修改作者 日期 修改内容<br/>
     * ================================================<br/>
     * 
     * @param joinPoint
     *            切点
     * @param rvt
     *            返回值
     * @throws Throwable
     *             异常 void
     *
     */
    @AfterReturning(returning = "rvt", pointcut = "serviceAspect()")
    public void recordLog(JoinPoint joinPoint, Object rvt) throws Throwable {

        HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();;
//        UserVo user = RequestContextProxy.getContextInfo();
        // 获取请求的ip
        String ip = ServletUtil.getClientIP(request);
        // 获取执行的类名
        String targetName = joinPoint.getTarget().getClass().getName();
        // 获取执行的方法名
        String targetMethod = joinPoint.getSignature().getName();
        // 获取操作系统相关信息
        UASparser uasParser = new UASparser(OnlineUpdater.getVendoredInputStream());
        UserAgentInfo userAgentInfo = uasParser.parse(request.getHeader("User-Agent"));
        // 获取客户端操作系统
        String osName = userAgentInfo.getOsName();
        // 获取客户端浏览器
        String browserName = userAgentInfo.getUaName();
        String result = this.getServiceMethodDescription(joinPoint);
        String[] opers = result.split(",");
        Object[] params = joinPoint.getArgs();
        SysLog record = new SysLog();
        record.setIpAddress(ip);
        record.setOperatorAccount("admin");
        record.setOperatorName("admin");
        record.setOperatorDate(LocalDateTime.now());
        // TODO 操作人所在组织
        record.setOperatorOrgCode("");
        record.setOperatorOrgName("");
        record.setOperatorSystem(osName);
        record.setOperatorBrowser(browserName);
        record.setClassName(targetName);
        record.setMethodName(targetMethod);
        record.setArguments(JSONUtil.toJsonStr(params));
        record.setModuleName(opers[0]);
        record.setDescription(opers[1]);
        record.setOperationLogType(ProjectConstants.OPERATION_LOG_TYPE_OPER);
        record.setOperationStatus(ProjectConstants.OPERATION_STATUS_NORMAL);
        record.setCreatorId(ProjectConstants.DEFAULT_SYSTEM);
        record.setCreator(ProjectConstants.DEFAULT_SYSTEM_NAME);
        record.setModifierId(ProjectConstants.DEFAULT_SYSTEM);
        record.setModifier(ProjectConstants.DEFAULT_SYSTEM_NAME);
        log.info(record.toString());
//        record.setId(cn.hutool.core.lang.UUID.randomUUID().toString());
        sysLogConsumerService.insert(record);

    }

    /**
     * 方法描述: [异常处理.]</br>
     * 初始作者:<br/>
     * 创建日期: 2019年3月29日-下午7:59:32<br/>
     * 开始版本: 2.0.0<br/>
     * =================================================<br/>
     * 修改记录：<br/>
     * 修改作者 日期 修改内容<br/>
     * ================================================<br/>
     * 
     * @param joinPoint
     *            切点
     * @param e
     *            异常
     * @throws Throwable
     *             异常 void
     *
     */
    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();;
//        UserVo user = RequestContextProxy.getContextInfo();
        // 获取请求的ip
        String ip = ServletUtil.getClientIP(request);
        // 获取执行的类名
        String targetName = joinPoint.getTarget().getClass().getName();
        // 获取执行的方法名
        String targetMethod = joinPoint.getSignature().getName();
        // 获取操作系统相关信息
        UASparser uasParser = new UASparser(OnlineUpdater.getVendoredInputStream());
        UserAgentInfo userAgentInfo = uasParser.parse(request.getHeader("User-Agent"));
        // 获取客户端操作系统
        String osName = userAgentInfo.getOsName();
        // 获取客户端浏览器
        String browserName = userAgentInfo.getUaName();
        String result = this.getServiceMethodDescription(joinPoint);
        String[] opers = result.split(",");
        Object[] params = joinPoint.getArgs();
        SysLog record = new SysLog();
        record.setIpAddress(ip);
        record.setOperatorAccount("admin");
        record.setOperatorName("admin");
        record.setOperatorDate(LocalDateTime.now());
        // TODO 操作人所在组织
        record.setOperatorOrgCode("");
        record.setOperatorOrgName("");
        record.setOperatorSystem(osName);
        record.setOperatorBrowser(browserName);
        record.setClassName(targetName);
        record.setMethodName(targetMethod);
        record.setArguments(JSONUtil.toJsonStr(params));
        record.setModuleName(opers[0]);
        record.setDescription(opers[1]);
        record.setOperationLogType(ProjectConstants.OPERATION_LOG_TYPE_OPER);
        record.setOperationStatus(ProjectConstants.OPERATION_STATUS_ERROR);
        record.setException(e.getMessage());
        record.setCreatorId(ProjectConstants.DEFAULT_SYSTEM);
        record.setCreator(ProjectConstants.DEFAULT_SYSTEM_NAME);
        record.setModifierId(ProjectConstants.DEFAULT_SYSTEM);
        record.setModifier(ProjectConstants.DEFAULT_SYSTEM_NAME);
        log.info(record.toString());
//        record.setId(cn.hutool.core.lang.UUID.randomUUID().toString());
        sysLogConsumerService.insert(record);
    }

    /**
     * 方法描述: [获取controller的操作信息.]</br>
     * 初始作者: br/>
     * 创建日期: 2019年3月29日-下午7:59:55<br/>
     * 开始版本: 2.0.0<br/>
     * =================================================<br/>
     * 修改记录：<br/>
     * 修改作者 日期 修改内容<br/>
     * ================================================<br/>
     * 
     * @param point
     *            切点
     * @return
     * @throws Exception
     *             异常 String
     *
     */
    @SuppressWarnings("rawtypes")
    public String getServiceMethodDescription(JoinPoint point) throws Exception {
        // 获取连接点目标类名
        String targetName = point.getTarget().getClass().getName();
        // 获取连接点签名的方法名
        String methodName = point.getSignature().getName();
        // 获取连接点参数
        Object[] args = point.getArgs();
        // 根据连接点类的名字获取指定类
        Class targetClass = Class.forName(targetName);
        // 获取类里面的方法
        Method[] methods = targetClass.getMethods();
        String result = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == args.length) {
                    String moduleName = method.getAnnotation(LogEvent.class).moduleName();
                    String description = method.getAnnotation(LogEvent.class).description();
                    result = moduleName + "," + description;
                    break;
                }
            }
        }
        return result;
    }
}
